{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "3f2853b6",
   "metadata": {},
   "source": [
    "## Agentic Travel Planner Chatbot\n",
    "\n",
    "- This application utilizes the **Gemini API** to function as a sophisticated travel planner.\n",
    "- Takes detailed traveler information and generating a comprehensive, strictly-formatted trip itinerary.\n",
    "- The user interface is built using Gradio, providing a convenient chat environment.\n",
    "- The final itinerary which the user is happy with, can be saved directly to a file via the model's tool-calling capability.\n",
    "\n",
    "### Key Features\n",
    "\n",
    "1. **Strict Output Generation:** Uses a detailed system prompt to force the LLM to provide 17 specific pieces of information for every itinerary.\n",
    "2. **Contextual Planning:** Reads traveler details from a travel_summary.txt file to ensure the itinerary is tailored to specific interests.\n",
    "3. **Gradio Chat UI:** Provides a simple, interactive chat interface for itinerary refinement.\n",
    "4. **Tool-Calling for Persistence:** Implements a function tool that the LLM can call to save the final generated itinerary to a file once the user is satisfied.\n",
    "\n",
    "### Prerequisites:\n",
    "\n",
    "1. You need a Gemini API key. This key should be set as an environment variable named GEMINI_API_KEY. \n",
    "2. Create summary.txt file. This file holds the context the model uses for planning. It is read once at startup.\n",
    "\n",
    "**Example travel_summary.txt as below:**\n",
    "\n",
    "- Vacation type: Family\n",
    "- Kids: One 4 year boy\n",
    "- Meals: Vegeterian\n",
    "- Interests: Walking, Hiking, Kids friendly walking trails, Kids friendly parks and activities, city exploration, beach, reading, pubs, cafes, historical places, Artistic and handmade items\n",
    "\n",
    "### Sample User prompts\n",
    "- First prompt: We are going to Barcelona in December during Christmans for a week. Can you plan my trip?\n",
    "- Second prompt: I am happy with your response. Save this to a file called trip.txt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "53cf381f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from openai import OpenAI\n",
    "import os\n",
    "import json\n",
    "import gradio as gr\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "faf9efdc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "load_dotenv(override=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "8401a6c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "google_api_key = os.getenv('GOOGLE_API_KEY')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a84deac5",
   "metadata": {},
   "outputs": [],
   "source": [
    "summary = \"\"\n",
    "with open(\"me/travel_summary.txt\", \"r\") as f:\n",
    "    summary = f.read()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d8947270",
   "metadata": {},
   "outputs": [],
   "source": [
    "system_prompt = f\"\"\"You operate as a Travel Planning Agent. \n",
    "You are given specific traveller profiles and interests in the input variable {summary}.\n",
    "\n",
    "MANDATORY REQUIREMENTS:\n",
    "\n",
    "Utilization of Information: You MUST incorporate the information regarding the travellers and their interests, as provided in {summary}, into the planning of the itinerary.\n",
    "\n",
    "Output Structure: Your response MUST contain a dedicated section for EACH of the following topics. \n",
    "If information for a section (e.g., address, news) is not provided, you must state that the information is \"Not Provided\" or \"Not Applicable\" (e.g., if no address is given, state \"Distance from Address: Not Provided\").\n",
    "\n",
    "MANDATORY CONTENT SECTIONS (MUST BE INCLUDED):\n",
    "\n",
    "Airport Transfer Plan: Detail the journey from the airport to the accommodation. MUST include suggested booking sites for tickets.\n",
    "Weather Forecast: Provide the expected weather conditions for the travel period.\n",
    "Essential Packing List: List critical items the travellers must carry.\n",
    "Places to Visit: List specific attractions. MUST include the distance from the accommodation address (if provided) and the best mode of transport from that address.\n",
    "Advance Booking Attractions: List all attractions that require or are highly recommended for advance ticket booking.\n",
    "Budget Travel Passes: Identify and detail any cheap travel passes or day passes available.\n",
    "Souvenir Shopping: Specify where to purchase authentic artistic souvenirs.\n",
    "Local Dining: Recommend the best restaurants in the area.\n",
    "Train Schedule (Airport): Provide train timings and frequency for travel to and from the airport.\n",
    "Train Ticket Information: Detail where and how to purchase train tickets.\n",
    "Local Transit Discounts: Detail available local travel passes and discounts (excluding the airport train).\n",
    "Cultural Reading Suggestions: Recommend fiction and non-fiction book titles related to the local culture.\n",
    "Media Suggestions: Recommend movies and/or music relevant to the visited location.\n",
    "Local Phrases: List common phrases or local slang for greetings and basic interactions.\n",
    "Local Alcoholic Beverage: Suggest a characteristic local alcoholic drink.\n",
    "Local News/Events: Report any recent or relevant local news or major events in the area.\n",
    "Local Activities: Suggest activities recommended by residents of the area. \"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "7eca6201",
   "metadata": {},
   "outputs": [],
   "source": [
    "def save_to_file(content, filename):\n",
    "    with open(filename, \"w\") as f:\n",
    "        f.write(content)\n",
    "    return {\"recorded\": \"ok\"}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "5d905a43",
   "metadata": {},
   "outputs": [],
   "source": [
    "save_to_file_json = {\n",
    "    \"name\": \"save_to_file\",\n",
    "    \"description\": \"Call this ONLY after the user explicitly confirms they are happy with the content and want to save it. Requires the full content and the desired filename.\",\n",
    "    \"parameters\": {\n",
    "        \"type\": \"object\",\n",
    "        \"properties\": {\n",
    "            \"content\": {\"type\": \"string\", \"description\": \"The complete, final text (the LLM's response) that the user is satisfied with and wants to save.\"},\n",
    "            \"filename\": {\"type\": \"string\", \"description\": \"The desired name of the file\"}\n",
    "        }\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "aca8269e",
   "metadata": {},
   "outputs": [],
   "source": [
    "tools = [{\"type\": \"function\", \"function\": save_to_file_json}]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "74343400",
   "metadata": {},
   "outputs": [],
   "source": [
    "def handle_tool_calls(tool_calls):\n",
    "    results = []\n",
    "    for tool_call in tool_calls:\n",
    "        tool_name = tool_call.function.name\n",
    "        arguments = json.loads(tool_call.function.arguments)\n",
    "        tool = globals().get(tool_name)\n",
    "        result = tool(**arguments) if tool else {}\n",
    "        results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n",
    "        \n",
    "    return results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "5423c1af",
   "metadata": {},
   "outputs": [],
   "source": [
    "def chat(message, history):\n",
    "    google = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n",
    "    model_name = \"gemini-2.0-flash\"\n",
    "    messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
    "    done = False\n",
    "    while not done:\n",
    "        response = google.chat.completions.create(model=model_name, messages=messages, tools=tools)\n",
    "        finish_reason = response.choices[0].finish_reason\n",
    "        print(finish_reason)\n",
    "        if finish_reason == \"tool_calls\":\n",
    "            message = response.choices[0].message\n",
    "            tool_calls = message.tool_calls\n",
    "            print(tool_calls)\n",
    "            result = handle_tool_calls(tool_calls)\n",
    "            messages.append(message)\n",
    "            messages.extend(result)\n",
    "        else:\n",
    "            done = True\n",
    "\n",
    "    return response.choices[0].message.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "d2988387",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* Running on local URL:  http://127.0.0.1:7861\n",
      "* To create a public link, set `share=True` in `launch()`.\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div><iframe src=\"http://127.0.0.1:7861/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": []
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\n",
    "gr.ChatInterface(\n",
    "    fn=chat, \n",
    "    title=\"Travel Planner\",\n",
    "    type=\"messages\",\n",
    "    description=\"Ask anything about the trip\").launch()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cdebd6d9",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
